/**
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "aicpu_prof.h"
#include "common/aicpu_hccl_common.h"
#include "debug/dfx/profiling/profiling_manager.h"

static uint32_t g_curLoopCnt = 0;
static uint32_t g_profTotalCnt = 0;
static AicpuComProf g_acprof[AC_MAX_PROF_LOOP];
uint8_t HcclCommProf::debugMode_ = 0;

uint32_t HcclCommProf::GetCurrentLoopCnt() {
    return g_curLoopCnt;
}

AicpuComProf *HcclCommProf::GetCurrentAicpuProf()
{
    return &g_acprof[g_curLoopCnt];
}

void HcclCommProf::SetCurrentProf(uint64_t launchTime)
{
    g_acprof[g_curLoopCnt].tid = syscall(__NR_gettid);
    g_acprof[g_curLoopCnt].launchEntryTime = launchTime;
}

void HcclCommProf::SetKfcTimeLine(KfcTimeLine kfcTimeLine)
{
    if (!HcclCommProf::NeedRecordTimeTaken()) {
        return;
    }
    AicpuComProf *acprof = HcclCommProf::GetCurrentAicpuProf();
    uint32_t recordIndex = acprof->workCnt;
    recordIndex = (recordIndex >= AC_MAX_PROF_COMM_CNT) ? (AC_MAX_PROF_COMM_CNT - 1) : recordIndex;
    switch (kfcTimeLine) {
        case KfcTimeLine::HCC_EXEC_START_TIME:
            acprof->commLoop[recordIndex].hccExecStartTime = GetCurCpuTimestamp(true);
            break;
        case KfcTimeLine::SEND_TASK_START_TIME:
            acprof->commLoop[recordIndex].sendTaskStartTime = GetCurCpuTimestamp(true);
            break;
        case KfcTimeLine::SEND_SQE_FINISH_TIME:
            acprof->commLoop[recordIndex].sendSqeFinishTime = GetCurCpuTimestamp(true);
            break;
        default:
            break;
    }
    return;
}

void HcclCommProf::SetDebugMode(uint8_t debugMode)
{
    debugMode_ = debugMode;
}

bool HcclCommProf::IsDebugModeEquals(const uint8_t mode)
{
    return debugMode_ == mode;
}

bool HcclCommProf::NeedRecordTimeTaken()
{
    return IsDebugModeEquals(MC2_DEBUG_TIME_TAKEN) || dfx::ProfilingManager::GetProfL1State();
}

AicpuComProf *HcclCommProf::GetaicpuProfInst() {
    return &g_acprof[0];
}

uint32_t HcclCommProf::GetProfTotalCnt() {
    return g_profTotalCnt;
}

void HcclCommProf::AddProfLoopCnt(uint32_t addCnt)
{
    if (!HcclCommProf::NeedRecordTimeTaken()) {
        return;
    }
    g_curLoopCnt += addCnt;
    g_curLoopCnt %= AC_MAX_PROF_LOOP;
    HCCL_INFO("g_curLoopCnt set to %u", g_curLoopCnt);
}

void HcclCommProf::AddProfTotalCnt(uint32_t addCnt) {
    g_profTotalCnt += addCnt;
}

void HcclCommProf::AddRcdCnt(uint32_t idx, int32_t sqeCnt) {
    if (idx >= AC_MAX_PROF_LOOP) {
        HCCL_ERROR("HcclCommProf AddRcdCnt idx %u overflow", idx);
        return;
    }
    g_acprof[idx].workCnt++;
    g_acprof[idx].fillSqeCnt += sqeCnt;
}

void HcclCommProf::OutputWorkProfLog(u32 workRcdCnt, u32 i)
{
    if (workRcdCnt <= 1) {
        return;
    }
    u32 s3Times = 0;
    u32 s3RtsqTimes = 0;
    u32 e2e = 0;
    AicpuComProf *acprof = &HcclCommProf::GetaicpuProfInst()[i];
    for (u32 j = 0; j < workRcdCnt; j++) {
        AicpuComProfCommLoop *commRcd = &acprof->commLoop[j];
        if (commRcd->aicpuOpEndTime != 0) {
            s3Times = commRcd->aicpuOpEndTime - commRcd->acitveStartTime;
            s3RtsqTimes = commRcd->aicpuOpEndTime - commRcd->waitExeStartTime;
            e2e = commRcd->aicpuOpEndTime - commRcd->aicpuOpExecStartTime;
        } else {
            s3Times = 0;
            s3RtsqTimes = 0;
            e2e = 0;
        }

        HCCL_RUN_INFO("RPC_WORK_PROF %u %u/%u : tid %llu, AddrTaskStart %lu, SendSqeFinished %lu, "
            "S2Times %lu, gap2-3 %lu, WorkTaskStart %lu, StartWaitEnd %lu, endTime %lu, S3Times %u, "
            "S3RtsqTimes %u, E2E %uns, dataLen %lu",
            HcclCommProf::GetProfTotalCnt() + i, ((j + 1 == workRcdCnt) && (workRcdCnt < acprof->workCnt)) ? acprof->workCnt : j + 1,
            acprof->workCnt, acprof->tid, commRcd->aicpuOpExecStartTime, commRcd->sendSqeFinishTime,
            commRcd->sendSqeFinishTime - commRcd->aicpuOpExecStartTime,
            commRcd->acitveStartTime - commRcd->sendSqeFinishTime, commRcd->acitveStartTime, commRcd->waitExeStartTime,
            commRcd->aicpuOpEndTime, s3Times, s3RtsqTimes, e2e, commRcd->dataLen);
    }
}

void HcclCommProf::OutputProfLog()
{
    if (!HcclCommProf::IsDebugModeEquals(MC2_DEBUG_TIME_TAKEN)) {
        return;
    }
    u32 threshold = AC_MAX_PROF_LOOP - 1;
    if (HcclCommProf::GetCurrentLoopCnt() >= threshold) {
        AicpuComProfCommLoop *commlastRcd;

        for (u32 i = 0; i <= threshold; i++) {
            AicpuComProf *acprof = &HcclCommProf::GetaicpuProfInst()[i];

            AicpuComProfCommLoop *commRcd = &acprof->commLoop[0];

            u32 workRcdCnt = acprof->workCnt > AC_MAX_PROF_COMM_CNT ? AC_MAX_PROF_COMM_CNT : acprof->workCnt;
            if (workRcdCnt > 1) {
                commlastRcd = &acprof->commLoop[workRcdCnt - 1];
            } else {
                commlastRcd = commRcd;
            }
            // rankId 公共函数获取 和ctx解耦
            // rankId
            HCCL_RUN_INFO(
                "RPC_PROF %u :clusterID %d, tid %llu, entry %lu, init_end %lu, S1Times %lu, gap1-2 %lu,"
                "AddrTaskStart(1st) %lu, SendSqeFinished(1st) %lu, S2Times %lu, (Filled %lu/%u, AllSend %lu/%u),"
                "GAP2-3(last-1st) %lu, WorkTaskStart(last) %lu, StartWaitEnd(last) %lu, endTime(last) %lu"
                ",S3Times %lu, S3RtsqTimes %lu, E2E %luns,"
                "traceSubmitTime %lu, traceCtxTime %lu,traceSqeTime %lu",
                HcclCommProf::GetProfTotalCnt() + i, acprof->clusterId, acprof->tid, acprof->launchEntryTime,
                acprof->commInitEndTime, acprof->commInitEndTime - acprof->launchEntryTime,
                commRcd->aicpuOpExecStartTime - acprof->commInitEndTime, commRcd->aicpuOpExecStartTime,
                commRcd->sendSqeFinishTime, commRcd->sendSqeFinishTime - commRcd->aicpuOpExecStartTime,
                acprof->fillSqeTimes, acprof->fillSqeCnt, acprof->sendSqeTimes, acprof->sendSqeBatch,
                commlastRcd->acitveStartTime - commRcd->sendSqeFinishTime, commlastRcd->acitveStartTime,
                commlastRcd->waitExeStartTime, commlastRcd->aicpuOpEndTime,
                commlastRcd->aicpuOpEndTime - commlastRcd->acitveStartTime,
                commlastRcd->aicpuOpEndTime - commlastRcd->waitExeStartTime,
                commlastRcd->aicpuOpEndTime - acprof->launchEntryTime, acprof->traceSubmitTime,
                acprof->traceCtxTime, acprof->traceSqeTime);

            HcclCommProf::OutputWorkProfLog(workRcdCnt, i);
            acprof->fillSqeCnt = 0;
            acprof->fillSqeTimes = 0;
            acprof->sendSqeBatch = 0;
            acprof->sendSqeTimes = 0;
            acprof->workCnt = 0;
            acprof->traceSubmitTime = 0;
            acprof->traceCtxTime = 0;
            acprof->traceSqeTime = 0;
        }
        HcclCommProf::AddProfTotalCnt(AC_MAX_PROF_LOOP);
    }
}